#include "MaggotApp.h"
#include <memory>
#include "CoreGlobals.h"
#include "../Utils/LogUtil.h"
#include "../Input/Input.h"
#include "../Thread/ThreadManager.h"
#include "../Render/Shader/ShaderMap.h"
#include "../Render/RenderState.h"
#include "../Utils/FileUtil.h"

// Make sure the shaderMap create before register shaders
// ShaderMap* GShaderMap = new ShaderMap();
// #include "../Render/Shader/GlobalShader.inl"

MaggotApp& MaggotApp::Get() {
	static MaggotApp maggotApp;
	return maggotApp;
}

MaggotApp::MaggotApp() {

	mCreateGameScene = nullptr;
	mDeleteGameScene = nullptr;
	// some init operate
	FileUtil::Init();
}


void MaggotApp::Run()
{
	mWindow = new MaggotApp::Window(this, mWindowTitle, mWidth, mHeight);

	ThreadManager::Get().Start();
}

void MaggotApp::ResizeWindow(uint32 width, uint32 height)
{
	mWidth = width;
	mHeight = height;
	if (GRenderState != nullptr)
	{
		// no need to recreate swapchain in here
		// wait for the present queue to do it
	}
}

MaggotApp::~MaggotApp() {

}

MaggotApp::Window::Window(MaggotApp* maggotApp, std::wstring title, uint32 width, uint32 height)
{
	setvbuf(stdout, NULL, _IONBF, 0);

	mMaggotApp = maggotApp;
	mWindowTitle = title;

	mhInstance = ::GetModuleHandle(NULL);

	WNDCLASSEX wndClass = { 0 };
	wndClass.cbSize = sizeof(WNDCLASSEX);
	wndClass.style = CS_HREDRAW | CS_VREDRAW;
	wndClass.lpfnWndProc = MaggotApp::Window::WndProc; 
	wndClass.cbClsExtra = 0;
	wndClass.cbWndExtra = 0;
	wndClass.hInstance = mhInstance;
	// wndClass.hIcon = (HICON)::LoadImage(NULL, L"icon.ico", IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_LOADFROMFILE); 
	wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
	wndClass.hbrBackground = (HBRUSH)GetStockObject(GRAY_BRUSH);
	wndClass.lpszMenuName = NULL;
	wndClass.lpszClassName = title.c_str();

	if (!RegisterClassEx(&wndClass))
	{
		LOGE(L"RegisterClassEx Window Failed");
		return;
	}

	mHwnd = CreateWindow(title.c_str(), title.c_str(),
		WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, static_cast<int>(width), static_cast<int>(height), NULL, NULL, wndClass.hInstance, NULL);

	MoveWindow(mHwnd, 0, 0, static_cast<int>(width), static_cast<int>(height), true);
	ShowWindow(mHwnd, SW_SHOW);
	UpdateWindow(mHwnd);

	Input::Get().SetWindow(mHwnd);
}

MaggotApp::Window::~Window()
{
	UnregisterClass(mWindowTitle.c_str(), mhInstance); 
}

LRESULT MaggotApp::Window::WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{

	switch (message)  
	{
	case WM_PAINT:
		ValidateRect(hwnd, NULL);
		break;
	case WM_SIZE: 
		MaggotApp::Get().ResizeWindow(static_cast<uint32>(LOWORD(lParam)), static_cast<uint32>(HIWORD(lParam)));
		break;
	case WM_DESTROY:
		PostQuitMessage(0);
		break;

	case WM_INPUT:
	case WM_LBUTTONDOWN:
	case WM_MBUTTONDOWN:
	case WM_RBUTTONDOWN:
	case WM_XBUTTONDOWN:
	case WM_LBUTTONUP:
	case WM_MBUTTONUP:
	case WM_RBUTTONUP:
	case WM_XBUTTONUP:
	case WM_MOUSEHOVER:
	case WM_MOUSEMOVE:
		Input::Get().ProcessMouse(message, wParam, lParam);
		break;
	case WM_MOUSEWHEEL:
		Input::Get().ProcessMouse(message, wParam, lParam);
		Input::Get().ProcessMouseWhell(message, wParam, lParam);
		break;
	case WM_MOUSEHWHEEL:
		Input::Get().ProcessMouseHWhell(message, wParam, lParam);
		break;
	case WM_KEYDOWN:
	case WM_SYSKEYDOWN:
	case WM_KEYUP:
	case WM_SYSKEYUP:
		Input::Get().ProcessKeyboard(message, wParam, lParam);
		break;
	case WM_ACTIVATEAPP:
		Input::Get().ProcessMouse(message, wParam, lParam);
		Input::Get().ProcessKeyboard(message, wParam, lParam);
		break;
	case WM_CHAR:
		if (wParam > 0 && wParam < 0x10000)
			Input::Get().ProcessInputChar((uint16)wParam);
		break;
	default:
		return DefWindowProc(hwnd, message, wParam, lParam);
	}
	return 0;
}

GameScene* MaggotApp::CreateGameScene()
{
	assert(mCreateGameScene != nullptr);
	return mCreateGameScene();
}

void MaggotApp::DeleteGameScene(GameScene* gameScene)
{
	assert(mDeleteGameScene != nullptr);
	mDeleteGameScene(gameScene);
}

std::vector<RenderProcess*> MaggotApp::CreateRenderProcess()
{
	assert(mCreateRenderProcess.size() > 0);
	std::vector<RenderProcess*> results;
	for (uint32 i = 0; i < mCreateRenderProcess.size(); ++i)
	{
		results.push_back(mCreateRenderProcess[i]());
	}
	return results;
}

void MaggotApp::DeleteRenderProcess(std::vector<RenderProcess*>& renderProcessMap)
{
	assert(mDeleteRenderProcess.size() > 0);
	for (uint32 i = 0; i < mDeleteRenderProcess.size(); ++i)
	{
		mDeleteRenderProcess[i](renderProcessMap[i]);
	}
}